﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using Insteon;
using Interfaces;
using Scheduling;

namespace Mesne
{
    public class SystemConfiguration : ISystemConfiguration
    {
        public event Action<string, string> ErrorOccured;

        public string GetConfig()
        {
            XmlDocument doc = new XmlDocument();
            doc.Load(AppSettings.Configuration.FilePath);
            XmlNode devices = doc.SelectSingleNode("//devices");
            if (devices == null)
            {
                return string.Empty;
            }
            else
            {
                return devices.OuterXml;
            }

        }

        private XmlDocument LoadConfig(string configPath, string sessionId = null)
        {
            XmlDocument doc = new XmlDocument();
            try
            {
                if (string.IsNullOrEmpty(configPath))
                {
                    doc.Load(AppSettings.Configuration.FilePath);
                }
                else
                {
                    doc.Load(configPath);
                }
            }
            catch (Exception ex)
            {
                if (ErrorOccured != null && sessionId != null)
                {
                    ErrorOccured(sessionId, ex.Message);
                }
            }
            return doc;
        }

        public void DeleteAllAndPersistOperations(List<IOperation> operations, string sessionId = null, string configPath = null)
        {
            XmlDocument doc = LoadConfig(configPath, sessionId);

            XmlNode ops = doc.SelectSingleNode("//operations");
            ops.RemoveAll();
            foreach (IOperation op in operations)
            {
                ops.AppendChild(CreateOperationElement(op, doc));
            }
            doc.Save(AppSettings.Configuration.FilePath);
        }

        public void PersistOperation(IOperation operation, string sessionId = null, string configPath = null)
        {
            XmlDocument doc = LoadConfig(configPath, sessionId);

            XmlNode operations = doc.SelectSingleNode("//operations");
            operations.AppendChild(CreateOperationElement(operation, doc));

            doc.Save(AppSettings.Configuration.FilePath);
        }

        public void PersistOperations(List<IOperation> ops, string sessionId = null, string configPath = null)
        {
            XmlDocument doc = LoadConfig(configPath, sessionId);

            XmlNode operations = doc.SelectSingleNode("//operations");
            foreach (IOperation op in ops)
            {
                operations.AppendChild(CreateOperationElement(op, doc));
            }
            doc.Save(AppSettings.Configuration.FilePath);
        }

        private XmlElement CreateOperationElement(IOperation operation, XmlDocument doc)
        {
            XmlElement op = doc.CreateElement("operation");
            XmlAttribute attr = doc.CreateAttribute("name");
            attr.Value = operation.Name;
            op.Attributes.Append(attr);

            if(operation.Years != null)
            {
                XmlElement years = doc.CreateElement("years");
                years.InnerText = operation.Years.Setting;
                op.AppendChild(years);
            }

            if (operation.Months != null)
            {
                XmlElement months = doc.CreateElement("months");
                months.InnerText = operation.Months.Setting;
                op.AppendChild(months);
            }

            if (operation.DaysOfYear != null)
            {
                XmlElement doy = doc.CreateElement("daysOfYear");
                doy.InnerText = operation.DaysOfYear.Setting;
                op.AppendChild(doy);
            }

            if (operation.DaysOfMonth != null)
            {
                XmlElement dom = doc.CreateElement("daysOfMonth");
                dom.InnerText = operation.DaysOfMonth.Setting;
                op.AppendChild(dom);
            }

            if (operation.DaysOfWeek != null)
            {
                XmlElement dow = doc.CreateElement("daysOfWeek");
                dow.InnerText = operation.DaysOfWeek.Setting;
                op.AppendChild(dow);
            }


            if (operation.Hours != null)
            {
                XmlElement hours = doc.CreateElement("hours");
                hours.InnerText = operation.Hours.Setting;
                op.AppendChild(hours);
            }

            if (operation.Minutes != null)
            {
                XmlElement minutes = doc.CreateElement("minutes");
                minutes.InnerText = operation.Minutes.Setting;
                op.AppendChild(minutes);
            }

            if (operation.Commands != null)
            {
                XmlElement commands = doc.CreateElement("commands");
                op.AppendChild(commands);

                foreach (Command cmd in operation.Commands)
                {
                    XmlElement command = doc.CreateElement("command");

                    if (cmd.DeviceID != null)
                    {
                        XmlAttribute id = doc.CreateAttribute("id");
                        id.Value = cmd.DeviceID;
                        command.Attributes.Append(id);
                    }

                    if (cmd.Cmd != null)
                    {
                        XmlAttribute cmmd = doc.CreateAttribute("cmd");
                        cmmd.Value = cmd.Cmd;
                        command.Attributes.Append(cmmd);
                    }

                    if (cmd.Level != null)
                    {
                        XmlAttribute level = doc.CreateAttribute("level");
                        level.Value = cmd.Level;
                        command.Attributes.Append(level);
                    }

                    commands.AppendChild(command);
                }
            }

            return op;
        }
        
        public List<IOperation> GetOperations(string sessionId = null, string configPath = null)
        {
            var retval = new List<IOperation>();
            XmlDocument doc = LoadConfig(configPath, sessionId);

            XmlNodeList operations = doc.SelectNodes("//operation");
            if (operations != null)
            {
                foreach (XmlNode node in operations)
                {
                    IOperation operation = new Operation();
                    operation.Name = node.Attributes["name"].Value;
                    foreach (XmlNode child in node.ChildNodes)
                    {
                        PopulateOperationChildInfo(child, ref operation);
                    }
                    retval.Add(operation);
                }
            }
            return retval;
        }

        public List<IDevice> GetDevices(string sessionId, string configPath = null)
        {
            var retval = new List<IDevice>();
            XmlDocument doc = LoadConfig(configPath, sessionId);
            
            XmlNodeList devices = doc.SelectNodes("//device");
            if (devices != null)
            {
                foreach (XmlNode node in devices)
                {
                    Device device = null;
                    switch (node.Attributes["type"].Value)
                    {
                        case "switch":
                            device = new Switch()
                            {
                                ID = node.Attributes["id"].Value,
                                Location = node.Attributes["location"].Value,
                                Name = node.Attributes["label"].Value
                            };
                            break;
                        case "dimmer":
                            device = new Dimmer()
                            {
                                ID = node.Attributes["id"].Value,
                                Location = node.Attributes["location"].Value,
                                Name = node.Attributes["label"].Value
                            };
                            break;
                        case "thermostat":
                            device = new Thermostat()
                            {
                                ID = node.Attributes["id"].Value,
                                Location = node.Attributes["location"].Value,
                                Name = node.Attributes["label"].Value
                            };
                            break;
                    }
                    retval.Add(device);
                }
            }
            return retval;
        }

        private void PopulateOperationChildInfo(XmlNode node, ref IOperation operation)
        {
            switch (node.Name)
            {
                case "years":
                    operation.Years = new TimeSpec(node.InnerText);
                    break;
                case "months":
                    operation.Months = new TimeSpec(node.InnerText);
                    break;
                case "daysOfYear":
                    operation.DaysOfYear = new TimeSpec(node.InnerText);
                    break;
                case "daysofMonth":
                    operation.DaysOfMonth = new TimeSpec(node.InnerText);
                    break;
                case "daysOfWeek":
                    operation.DaysOfWeek = new TimeSpec(node.InnerText);
                    break;
                case "hours":
                    operation.Hours = new TimeSpec(node.InnerText);
                    break;
                case "minutes":
                    operation.Minutes = new TimeSpec(node.InnerText);
                    break;
                case "commands":
                    foreach (XmlNode cmdNode in node.ChildNodes)
                    {
                        System.IO.StreamWriter writer = new System.IO.StreamWriter(@"C:\mesne\mesne.log");
                        try
                        {
                            string id = cmdNode.Attributes["id"].Value;
                            writer.WriteLine("1");
                            string cmd = cmdNode.Attributes["cmd"].Value;
                            writer.WriteLine("2");
                            string level = cmdNode.Attributes["level"].Value;
                            writer.WriteLine("3");
                            Command command = new Command(id, cmd, level);
                            writer.WriteLine("4");
                            operation.Commands.Add(command);
                        }
                        catch
                        {
                            break;
                        }
                        finally
                        {
                            writer.Close();
                        }
                    }
                    break;
                default:
                    System.Diagnostics.Debug.Assert(false, "Unknown Node Type.");
                    break;
            }
        }
    }
}
